Updating Native PCI Device Drivers for PCI Power Management Compliance
Contents
I. Introduction
II. New PCI Driver Requirements
III. Power Handlers
A. Registration
B. Using Power Handlers
IV. Overview of Sleep and Wake for PCI Drivers
A. Sleep
B. Wake
C. Potential problems
V. Notes
I. Introduction
The most recent Apple professional desktop CPU's have the capability to remove power from and/or stop the clocks to the PCI expansion slots when the CPU is put into a Power Management Sleep state. The implication of this new capability is that all drivers that control the devices present in the expansion slots must be able to recover their device’s functionality once power is restored after a Power Management Wake event. If any driver is incapable of recovering its device’s functionality, then System Software will not allow power to be removed from the PCI expansion slots and, therefore, the CPU will not be allowed to operate from its standby power supply during the Sleep state.
This new power management feature is implemented according to the specifications published by the PCI Special Interest Group for PCI Bus Power Management. (1)(2)
II. New PCI Driver Requirements
In order to ensure that the System SW can recover from removing power from the PCI expansion bus, it looks for an indication from all PCI device drivers that each and every driver can recover once PCI bus power has been restored (during Wake).
To notify the System Software that a a particular driver can recover successfully from PCI power shut-off, a driver must implement a new Power Management “Power Handler”, which it must register with the Power Manager when loaded.
III. Power Handlers
The Sleep and Wake process for PCI device drivers is now centralized around a new mechanism called a Power Handler. Power handler support is present in version 2.0 (and later) of the Power Manager. Power handlers work in a similar way to the traditional SleepQProcs but with the following differences:
• Power handlers are called later in the sleep process and earlier in the
wake process compared to the classic SleepQProcs;
• Power handlers are expected to handle a new variety of sleep and wake messages;
• CPU interrupts are disabled when a Power handler is called with a
kSleepDemand, kDozeDemand, or kSuspendDemand message; CPU interrupts are
not disabled during a kSleepRequest, kDozeRequest, or kSuspendRequest message.
A. Registration
A Driver’s Power Handler is registered via the following API:
EXTERN_API_C ( OSStatus )
AddDevicePowerHandler( RegEntryIDPtr regEntryID,
PowerHandlerProcPtr handler,
UInt32 refCon,
char* deviceType );
All four (4) parameters are inputs.
regEntryID pointer to the device’s Name Registry Entry
handler pointer to a power handler function (PowerPC code--not a routine descriptor)
refCon optional value passed in; will be passed back to the power handler.
deviceType* an optional parameter, where the deviceType is specified, such as “scsi” or “ata”.
It is easiest and most effective to simply pass NULL as this parameter. If passing
NULL, the Power Manager will automatically determine the device type.
Before trying to register a Power Handler by calling AddDevicePowerHandler, be sure that the new Power Manager is available. By “weak-linking” to the latest version of the DriverServicesLib and comparing AddDevicePowerHandler to kUnresolvedCFragSymbolAddress and only proceeding if the check passes, the driver should be safe:
if ( (Ptr)AddDevicePowerHandler != (Ptr)kUnresolvedCFragSymbolAddress )
{
gAddDevicePwrHandlerExists = true;
}
else
{
gAddDevicePwrHandlerExists = false;
}
“Weak-linking” is accomplished differently depending upon the development system. Metrowerks offers a checkbox (“Import Weak”) from the Project Inspector window for a particular shared library. MPW’s PPCLink tool has a “-weaklib name[,name]...” command line option for specifying the import lib name(s) as weak.
If a driver is finished with a registered Power Handler, it can be removed by the following PowerManager API method:
message indicates what the Power handler needs to do. Messages that could
be received:
kGetPowerInfo the Driver should report back what types of
power features are supported; when this
message is received, the param is a pointer
to aDevicePowerInfo structure and should
be filled in according to the device’s power
capabilities
kSleepRequest PCI devices should complete any pending
I/O in order to prepare for PCI bus power
to be removed.
kSleepDemand PCI devices should save their device
state. PCI bus power is about to be removed.
CPU interrupts are disabled at this stage.
kSleepWakeUp PCI devices should restore their device state.
CPU interrupts are disabled at this stage.
kSleepRevoke PCI devices should reverse any effects of an earlier
kSleepRequest message.
kGetPowerLevel return the device’s current power level.
kSetPowerLevel set the device’s current power level.
kDeviceInitiatedWake the Driver should report back, via param, if it
caused a wake event
kWakeToDoze the Driver should perform the same steps as
kSleepWakeUp unless it supports a partial wake
state. Do NOT ignore or return
kPowerMgtMessageNotHandled!
This message is sent in lieu of kSleepWakeUp in
the case of a partial wake.
kDozeToFullWakeUp if the Driver handles kWakeToDoze differently
from kWakeToSleep then it must perform
remaining steps to reach a full wakeup state.
Otherwise ignore.
param used to pass back information to the caller; in the Power Handler’s case, this
information could be a DevicePowerInfo structure, that should be filled with
appropriate values for the device. See the sample code for an example of a proper
response. For the kDeviceInitiatedWake it is a simple numeric value indicating if
the device caused the wake event and, if so, how "fully" the Power Mgr should
wake the machine.
refCon value that was passed to AddDevicePowerHandler.
regEntryID A pointer to the device’s Name Registry Entry
As with Power Handlers registered by other non-ndrv software, appropriate errors must be returned:
• If csCode or message = kSleepRequest/kDozeRequest/kSuspendRequest and the driver wishes
to deny the request, it returns a newly-defined kPowerMgtRequestDenied error to the caller. If not,
then the driver returns noErr (as with the high-level classic SleepQProc queue).
• If csCode or message = kSleepDemand/ kDozeDemand/ kSuspendDemand then any result returned
is ignored.
• Any power management csCode or message not handled by the driver should be indicated by the
driver returning kPowerMgtMessageNotHandled.
IV. Overview of Sleep and Wake for PCI Drivers
Before the System Software is able to request or demand that a device enter a Sleep state, the driver needs to register a Power Handler with the Power Manager as described above under “Power Handlers”. Usually, this happens during driver initialization.
The Power Handler queue is serviced after the high-level classic SleepQProc queue is serviced. This is useful for allowing devices to provide functionality to the system (and all registered classic SleepQProcs) until the last possible moment before preparing devices for Sleep.
The driver is required, when receiving a kSleepDemand message, to set its device into the D3cold state. When the PCI bus power cannot be powered off, the Power Manager will send a Power Handler a kDozeRequest and/or kDozeDemand message to put the system into a type of Sleep that we have today for desktops; the PCI bus remains fully powered. Most PCI slot devices can ignore a kDozeRequest and/or kDozeDemand message since PCI bus power remains On during the Doze state.
Apple discourages a driver from setting the device state to D3hot when the Power Manager enters the Doze state. The hardware does not assert the PCI Bus Segment Reset (RST#) signal to the device when Waking from the Doze state; however, the RST# signal is asserted to the device when Waking from the Sleep state after PCI bus power has been restored.
A. Sleep
When the system is about to be put into a Sleep state, the Power Manager will eventually invoke the Power Handler that the driver had previously registered. First, the Power Handler will be asked to provide its power capabilities information in the form of a kGetPowerInfo message. In this case, the “param” parameter is a DevicePowerInfo structure. It has already been filled out with information from the PCI config space (PMIS 1.1). Drivers only need to change information in the structure if there is a need to override the pre-determined hardware values. Then, the Power Handler will be asked, via a kSleepRequest message, to prepare the device for entering the Sleep state. Note that kSleepRequest messages are processed with CPU interrupts enabled. The Power Handler must make its best-effort to prepare the device for having power removed; this includes resolving the following:
• Any pending I/O should be completed.
IMPORTANT: The driver should remain ready to process additional I/O requests until the
kSleepDemand message is received. Do not suspend any I/O at kSleepRequest time
because a page fault during the sleep process could lead to dead lock.
The Power Handler may deny a kSleepRequest message; if any Power Handler denies a kSleepRequest message, all previously invoked PCI Power Handlers will be called again with a kSleepRevoke message to reverse any effects from processing their kSleepRequest message. When a Power Handler denies a kSleepRequest message, unlike classic SleepQProcs, the Sleep process will continue and the CPU will be put into a Sleep state but with the PCI bus power remaining On. Unfortunately, when the PCI bus power is On during Sleep the CPU cannot operate with its standby power supply.
When the Power handler wishes to deny a kSleepRequest message, it must return the kPowerMgtRequestDenied code.
If each PCI Power Handler positively acknowledges the kSleepRequest message, the Power Manager will then send each Power handler a kSleepDemand message. NOTE that CPU interrupts are disabled at this point. The Power Handler must do the following:
• Again, any pending I/O should be completed and subsequent I/O should be suspended.
• The driver's device interrupts should be turned off and an error returned in response to subsequent
I/O requests.
• Any device control registers and private (beyond base+64 bytes) PCI configuration space on the device
need to be saved. Apple System SW will save/restore only the first 64 bytes of the device's PCI
configuration space. The driver is responsible for saving (and restoring) any other volatile memory
on the device.
• If the device is capable of operating in a low power mode, then set the device to operate in the lowest
power mode that is feasible for the device.
• Return noErr when exiting the Power Handler.
After each PCI slot device driver has saved the device’s state, the PCI bus power will be removed.
B. Wake
When the system is coming out of Sleep and making progress toward the On state, the Power Manager will service the Power Handler queues before servicing any registered classic SleepQProcs. When the Handler receives a kSleepWakeUp message, it needs to reverse any action taken to bring its device to the Sleep state. Specifically:
• Restore any saved control registers and/or any private (beyond base+64 bytes) PCI configuration space.
• Enable the device’s interrupt(s).
• Restart any pending I/O that was suspended during the kSleepDemand message.
C. Potential problems
Unlike the classic SleepQProcs, Power Handlers are not allowed to allocate memory or use any system services which may cause memory to move. Additionally, Handlers should not perform any action that would cause a page fault. Finally, remember that CPU interrupts are disabled when a Power Handler is called with a kSleepDemand, kDozeDemand, or kSuspendDemand message.
V. Notes
(1) See “PCI Bus Power Management Interface Specification”, v1.1, Dec 1998, PCI Special Interest Group.
(2) See “PCI Power Management Working Group ECR”, May 1997, Intel Corporation.